/*
 Copyright 2007 Razvan Taranu

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
package generators;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaSource;
import com.thoughtworks.qdox.model.Type;

public class PhpHelper {
    private static Log log = LogFactory.getLog(PhpHelper.class);

    private static File rootDirectory;
    private static File beansDirectory;
    private static File jsonizersDirectory;
    private static File servicesDirectory;

    public static void configure(JavaDocBuilder docBuilder, Configuration config) {
        String outputPath = config.getString("php.output.path");

        rootDirectory = new File(outputPath);
        if (rootDirectory.exists() && false == rootDirectory.isDirectory()) {
            throw new RuntimeException(rootDirectory + " exists and is not a directory");
        }
        rootDirectory.mkdirs();

        beansDirectory = new File(outputPath + "/beans");
        if (beansDirectory.exists() && false == beansDirectory.isDirectory()) {
            throw new RuntimeException(beansDirectory + " exists and is not a directory");
        }
        beansDirectory.mkdirs();

        jsonizersDirectory = new File(outputPath + "/jsonizers");
        if (jsonizersDirectory.exists() && false == jsonizersDirectory.isDirectory()) {
            throw new RuntimeException(jsonizersDirectory + " exists and is not a directory");
        }
        jsonizersDirectory.mkdirs();

        servicesDirectory = new File(outputPath + "/services");
        if (servicesDirectory.exists() && false == servicesDirectory.isDirectory()) {
            throw new RuntimeException(servicesDirectory + " exists and is not a directory");
        }
        servicesDirectory.mkdirs();
    }

    public static void writeBean(String beanName, String content) {
        File outputFile = new File(beansDirectory, beanName + ".php");
        log.debug("writing bean " + beanName + " to " + outputFile);
        Utils.writeFile(content, outputFile);
    }

    /**
     * 
     * @param beanName
     *            name of the bean (not the name of the jsonizer class)
     * @param content
     */
    public static void writeJsonizer(String beanName, String content) {
        File outputFile = new File(jsonizersDirectory, beanName + "Jsonizer.php");
        log.debug("writing jsonizer for " + beanName + " to " + outputFile);
        Utils.writeFile(content, outputFile);
    }

    /**
     * 
     * @param serviceName
     *            name of the service interface
     * @param content
     */
    public static void writeService(String serviceName, String content) {
        File outputFile = new File(servicesDirectory, serviceName + ".php");
        log.debug("writing service for " + serviceName + " to " + outputFile);
        Utils.writeFile(content, outputFile);
    }

    public static String valueToPhp(String value, Type type) {
        String className = type.getValue();
        boolean isArray = type.isArray();
        String genericClass = Beans.getGenericClassName(type);

        String template = "@@jsonizer@@Jsonizer::toPhp@@type@@(@@value@@)";

        log.debug("toPhp className: [" + className + "] isArray: [" + isArray + "] genericClass: [" + genericClass + "]");
        String jsonizer = jsonizer(template, className, isArray, genericClass);

        // jsonizer = ObjectJsonizer::toPhpString(@@value@@)

        if (Beans.isSerializable(className) || (List.class.getName().equals(className) && Beans.isSerializable(genericClass))
                || (Map.class.getName().equals(className) && Beans.isSerializable(genericClass))) {
            jsonizer = jsonizer.replace("@@value@@", "@@value@@, $helper");
        }

        return jsonizer.replace("@@value@@", value);
    }

    public static String valueToJson(String value, Type type) {
        String className = type.getValue();
        boolean isArray = type.isArray();
        String genericClass = Beans.getGenericClassName(type);

        String template = "@@jsonizer@@Jsonizer::toJson@@type@@(@@value@@)";

        log.debug("toJson className: [" + className + "] isArray: [" + isArray + "] genericClass: [" + genericClass + "]");
        String jsonizer = jsonizer(template, className, isArray, genericClass);

        // jsonizer = ObjectJsonizer::toJsonString(@@value@@)

        if (Beans.isSerializable(className) || (List.class.getName().equals(className) && Beans.isSerializable(genericClass))
                || (Map.class.getName().equals(className) && Beans.isSerializable(genericClass))) {
            jsonizer = jsonizer.replace("@@value@@", "@@value@@, $helper");
        }

        return jsonizer.replace("@@value@@", value);
    }

    private static final Map<String, String> objects = new HashMap<String, String>();
    static {
        objects.put("java.lang.Character", "Char");
        objects.put("java.lang.String", "String");
        objects.put("java.lang.Boolean", "Boolean");
        objects.put("java.lang.Short", "Short");
        objects.put("java.lang.Integer", "Integer");
        objects.put("java.lang.Long", "Long");
        objects.put("java.lang.Float", "Float");
        objects.put("java.lang.Double", "Double");
        objects.put("java.util.Date", "Date");
    }

    private static final Map<String, String> primitives = new HashMap<String, String>();
    static {
        primitives.put("char", "Char");
        primitives.put("boolean", "Boolean");
        primitives.put("short", "Short");
        primitives.put("int", "Integer");
        primitives.put("long", "Long");
        primitives.put("float", "Float");
        primitives.put("double", "Double");
    }

    public static String jsonizer(String template, String className, boolean isArray, String genericClass) {
        String jsonizer, type;

        if (primitives.containsKey(className)) {
            jsonizer = (isArray) ? "PrimitiveArray" : "Primitive";
            type = primitives.get(className);

        } else if (objects.containsKey(className)) {
            jsonizer = (isArray) ? "ObjectArray" : "Object";
            type = objects.get(className);

        } else if (List.class.getName().equals(className)) {
            if (objects.containsKey(genericClass)) {
                jsonizer = "ObjectArray";
                type = objects.get(genericClass);

            } else {
                if (false == Beans.isSerializable(genericClass)) {
                    throw new RuntimeException("unknown typeArg: " + genericClass);
                }
                jsonizer = JavaHelper.getBeanName(genericClass);
                type = "Array";

            }

        } else if (Map.class.getName().equals(className)) {
            if (objects.containsKey(genericClass)) {
                jsonizer = "Map";
                type = objects.get(genericClass);

            } else {
                if (false == Beans.isSerializable(genericClass)) {
                    throw new RuntimeException("unknown typeArg: " + genericClass);
                }
                jsonizer = JavaHelper.getBeanName(genericClass);
                type = "Map";

            }
        } else {
            if (false == Beans.isSerializable(className)) {
                throw new RuntimeException("unknown javaType: " + className);
            }
            jsonizer = JavaHelper.getBeanName(className);
            type = (isArray) ? "Array" : "";
        }

        template = template.replace("@@jsonizer@@", jsonizer);
        template = template.replace("@@type@@", type);

        return template;
    }

    private static final Map<String, String> phpJsonizers;
    static {
        phpJsonizers = new HashMap<String, String>();

        phpJsonizers.put("JsonizerHelper.php", "templates/php/jsonizers/JsonizerHelper.tpl");

        phpJsonizers.put("PrimitiveJsonizer.php", "templates/php/jsonizers/PrimitiveJsonizer.tpl");
        phpJsonizers.put("PrimitiveArrayJsonizer.php", "templates/php/jsonizers/PrimitiveArrayJsonizer.tpl");

        phpJsonizers.put("ObjectJsonizer.php", "templates/php/jsonizers/ObjectJsonizer.tpl");
        phpJsonizers.put("ObjectArrayJsonizer.php", "templates/php/jsonizers/ObjectArrayJsonizer.tpl");

        phpJsonizers.put("MapJsonizer.php", "templates/php/jsonizers/MapJsonizer.tpl");
    }

    public static void copyTemplates() {
        log.info("copying PHP templates");

        List<String> beans = new ArrayList<String>();
        for (BeanInfo bean : Beans.beanList) {
            beans.add(bean.getJavaClass().getName());
        }

        String requireBeans;
        String requireBeanJsonizers;
        if (beans.isEmpty()) {
            requireBeans = "";
            requireBeanJsonizers = "";
        } else {
            requireBeans = "require_once 'beans/";
            requireBeans += StringUtils.join(beans, ".php';\nrequire_once 'beans/");
            requireBeans += ".php';";

            requireBeanJsonizers = "require_once 'jsonizers/";
            requireBeanJsonizers += StringUtils.join(beans, "Jsonizer.php';\nrequire_once 'jsonizers/");
            requireBeanJsonizers += "Jsonizer.php';";
        }

        Set<JavaSource> serviceSources = JavaHelper.getRemoteServiceSources();

        List<String> services = new ArrayList<String>();
        for (JavaSource serviceSource : serviceSources) {
            services.add(serviceSource.getClasses()[0].getName());
        }

        String requireServices;
        if (services.isEmpty()) {
            requireServices = "";
        } else {
            requireServices = "require_once 'services/";
            requireServices += StringUtils.join(services, ".php';\nrequire_once 'services/");
            requireServices += ".php';";

        }

        String lacerate = Utils.readRessource("templates/php/Lacertae.inc.tpl");
        lacerate = lacerate.replaceAll("@@requireBeans@@", requireBeans);
        lacerate = lacerate.replaceAll("@@requireBeanJsonizers@@", requireBeanJsonizers);
        lacerate = lacerate.replaceAll("@@requireServices@@", requireServices);

        Utils.writeFile(lacerate, new File(rootDirectory, "Lacertae.inc.php"));

        for (String outputFilename : phpJsonizers.keySet()) {
            String ressourcePath = phpJsonizers.get(outputFilename);

            File fileOut = new File(jsonizersDirectory, outputFilename);

            String jsonizerSource = Utils.readRessource(ressourcePath);
            Utils.writeFile(jsonizerSource, fileOut);
        }
    }
}
